package com.clei.utils;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.EncodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;

/**
 * qrcode util
 *
 * @author Y
 * @date 2024-01-02
 **/
public class QRCodeUtil {
    /**
     * 默认宽度
     */
    private static final int WIDTH = 300;

    /**
     * 默认高度
     */
    private static final int HEIGHT = 300;

    /**
     * 默认文件格式
     */
    private static final String FORMAT = "png";

    /**
     * 默认参数
     */
    private static final Map<EncodeHintType, Object> HINTS = new HashMap<>(4);

    /**
     * 默认颜色参数
     * 黑红
     * 使用红黑会报com.google.zxing.NotFoundException 应该是个bug
     */
    private static final MatrixToImageConfig COLOR = new MatrixToImageConfig(-16777216, -65536);

    static {
        HINTS.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
        // 容错等级 L、M、Q、H 其中 L 为最低, H 为最高
        HINTS.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 二维码与图片边距
        HINTS.put(EncodeHintType.MARGIN, 2);
    }

    /**
     * toBufferedImage
     *
     * @param content 二维码内容
     */
    public static BufferedImage toBufferedImage(String content) {
        return toBufferedImage(content, WIDTH, HEIGHT);
    }

    /**
     * toBufferedImage
     *
     * @param content 二维码内容
     * @param width   宽
     * @param height  高
     */
    public static BufferedImage toBufferedImage(String content, int width, int height) {
        try {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, HINTS);
            return MatrixToImageWriter.toBufferedImage(bitMatrix, COLOR);
        } catch (Exception e) {
            PrintUtil.log("toBufferedImage error ", e);
            return null;
        }
    }

    /**
     * writeToStream
     *
     * @param content 二维码内容
     * @param stream  输出流
     */
    public static void writeToStream(String content, OutputStream stream) {
        writeToStream(content, stream, WIDTH, HEIGHT);
    }

    /**
     * writeToStream
     *
     * @param content      二维码内容
     * @param outputStream 输出流
     * @param width        宽
     * @param height       高
     */
    public static void writeToStream(String content, OutputStream outputStream, int width, int height) {
        try {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, HINTS);
            MatrixToImageWriter.writeToStream(bitMatrix, FORMAT, outputStream, COLOR);
        } catch (Exception e) {
            PrintUtil.log("writeToStream error ", e);
        }
    }

    /**
     * writeToFile
     *
     * @param content 二维码内容
     * @param path    文件保存路径
     */
    public static void writeToFile(String content, String path) {
        writeToFile(content, path, WIDTH, HEIGHT);
    }

    /**
     * writeToFile
     *
     * @param content 二维码内容
     * @param file    文件保存路径
     */
    public static void writeToFile(String content, File file) {
        writeToFile(content, file.toPath(), WIDTH, HEIGHT);
    }

    /**
     * writeToFile
     *
     * @param content 二维码内容
     * @param path    文件保存路径
     * @param width   宽
     * @param height  高
     */
    public static void writeToFile(String content, String path, int width, int height) {
        writeToFile(content, new File(path).toPath(), width, height);
    }

    /**
     * writeToFile
     *
     * @param content 二维码内容
     * @param path    文件保存路径
     * @param width   宽
     * @param height  高
     */
    public static void writeToFile(String content, Path path, int width, int height) {
        try {
            String fileName = path.toFile().getName();
            String format = FileUtil.getSuffix(fileName);
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, HINTS);
            MatrixToImageWriter.writeToPath(bitMatrix, format, path, COLOR);
        } catch (Exception e) {
            PrintUtil.log("writeToFile error ", e);
        }
    }

    /**
     * readFromFile
     *
     * @param filePath filePath
     * @return content
     */
    public static String readFromFile(String filePath) {
        try {
            BufferedImage image = ImageIO.read(new File(filePath));
            return readFromBufferedImage(image);
        } catch (Exception e) {
            PrintUtil.log("readFromFile error ", e);
        }
        return null;
    }

    /**
     * readFromFile
     *
     * @param file File
     * @return content
     */
    public static String readFromFile(File file) {
        try {
            BufferedImage image = ImageIO.read(file);
            return readFromBufferedImage(image);
        } catch (Exception e) {
            PrintUtil.log("readFromFile error ", e);
        }
        return null;
    }

    /**
     * readFromBufferedImage
     *
     * @param inputStream InputStream
     * @return content
     */
    public static String readFromStream(InputStream inputStream) {
        try {
            BufferedImage image = ImageIO.read(inputStream);
            return readFromBufferedImage(image);
        } catch (Exception e) {
            PrintUtil.log("readFromStream error ", e);
        }
        return null;
    }

    /**
     * readFromBufferedImage
     *
     * @param image BufferedImage
     * @return content
     */
    public static String readFromBufferedImage(BufferedImage image) {
        try {
            // 将图像转换为二进制位图源
            LuminanceSource source = new BufferedImageLuminanceSource(image);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            QRCodeReader reader = new QRCodeReader();
            Result result = reader.decode(bitmap);
            return result.getText();
        } catch (Exception e) {
            PrintUtil.log("readFromBufferedImage error ", e);
        }
        return null;
    }
}
